package com.pwc.conversionTool.biz.sendSoap;

import static com.pwc.GAE.blobstore.BlobstoreUtil.deleteBlob;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

import javax.xml.soap.SOAPException;

import org.dom4j.DocumentException;

import com.googlecode.objectify.Key;
import com.pwc.conversionTool.XML.XMLBase;
import com.pwc.conversionTool.biz.DataAdaptorBiz;
import com.pwc.conversionTool.biz.ProjectBiz;
import com.pwc.conversionTool.biz.TaskBiz;
import com.pwc.conversionTool.dao.LogDao;
import com.pwc.conversionTool.dataAdaptor.DataAdaptor;
import com.pwc.conversionTool.dataAdaptor.DataFilter;
import com.pwc.conversionTool.dataAdaptor.FormulaProcess;
import com.pwc.conversionTool.dataAdaptor.IDataAdaptor;
import com.pwc.conversionTool.entity.DataType;
import com.pwc.conversionTool.entity.Formula;
import com.pwc.conversionTool.entity.Operation;
import com.pwc.conversionTool.entity.SoapConnection;
import com.pwc.conversionTool.entity.Task;
import com.pwc.conversionTool.entity.TaskLog;
import com.pwc.soapUtil.SoapUtil;

public class TaskProcess {
	
	private TaskLog taskLog=null;
	
	private TaskBiz taskBiz = null;
	
	private ProjectBiz projectBiz = null;
	
	private Operation operation=null;
	
	private List<IDataAdaptor> dataAdaptors = new ArrayList<IDataAdaptor>();
	
	private LogDao logDao=new LogDao();
	
	private Task task=null;
	
	private IDataAdaptorProcess dataAdaptorProcess=null;
	
	private SoapUtil soapUtil = new SoapUtil();
	
	private List<String> payloads = new ArrayList<String>();
	
	private DataAdaptorBiz dataAdaptorBiz =new DataAdaptorBiz();
	
	private Formula formula;
	
	public void initial(Task task)
	{
		try {
			
			taskLog=new TaskLog(task.getTaskName(),task.getKey());
			taskBiz = new TaskBiz();
			projectBiz = new ProjectBiz();
			this.task=task;
			Key<Operation> operationkey = task.getOperation();
			operation = projectBiz.getOperationByKey(operationkey);
			formula=dataAdaptorBiz.getFormula(operation.getDataAdaptorData());
		} catch (Exception e) {
			taskLog.addMessage("error in initial task process ");
			taskLog.addMessage(e.getMessage());
			taskLog.addMessage(Arrays.toString(e.getStackTrace()));
			e.printStackTrace();
		}
	}


	/**
	 * main function for running process
	 * @return
	 * @throws IOException 
	 * @throws DocumentException 
	 * @throws SOAPException 
	 * @throws UnsupportedOperationException 
	 */
	public boolean doSendProcess() throws IOException, DocumentException, UnsupportedOperationException, SOAPException {
		
		try {
			if(!getDataAdaptors())
			{
				taskLog.addMessage("error in get data adaptors");
				
				return false;
			}else if(!doFilter())
			{
				taskLog.addMessage("error in do filter");
				
				return false;
			}else if(!doFormula())
			{
				taskLog.addMessage("error in do formula");
				
				return false;
			}else if(!sendPayload())
			{
				taskLog.addMessage("error in sending payload");
				
				return false;
			}else {
				taskLog.addMessage("sending payload success! Task over!");
				
				return true;
			}
		} catch (Exception e) {
			taskLog.addMessage("error in do Send Process ");
			taskLog.addMessage(e.getMessage());
			taskLog.addMessage(Arrays.toString(e.getStackTrace()));
			e.printStackTrace();
			
			return false;
		}
	}
	
	
	/**
	 * get data adaptor by different type
	 * @return
	 * @throws IOException 
	 */
	private boolean getDataAdaptors() throws IOException
	{
		
		try {
			
			DataAdaptorProcess dataAdaptorProcess=new DataAdaptorProcess(task,taskLog);
			
			dataAdaptors=dataAdaptorProcess.getDataAdaptors();
			
		} catch (Exception e) {
			taskLog.addMessage("error in initial task process ");
			taskLog.addMessage(e.getMessage());
			taskLog.addMessage(Arrays.toString(e.getStackTrace()));
			taskLog.setHasError(true);
			e.printStackTrace();
		}
		
		return !taskLog.isHasError();
	}
	

	
	/**
	 * filter 
	 * @return
	 */
	private boolean doFilter()
	{
		try {
			List<IDataAdaptor> filtered=new ArrayList<IDataAdaptor>();
			
			for (IDataAdaptor iDataAdaptor : dataAdaptors) {
				
				DataFilter datafilter = new DataFilter(iDataAdaptor, formula);
				IDataAdaptor result=datafilter.Process();
				filtered.add(result);
			}
			dataAdaptors=filtered;
			return true;
		} catch (Exception e) {
			taskLog.addMessage("error in do filter ");
			taskLog.addMessage(e.getMessage());
			taskLog.addMessage(Arrays.toString(e.getStackTrace()));
			taskLog.setHasError(true);
			e.printStackTrace();
		}
		
		return false;
	}
	
	
	private boolean doFormula()
	{
		try {
			List<IDataAdaptor> formulaAdaptors=new ArrayList<IDataAdaptor>();
			
			for (IDataAdaptor iDataAdaptor : dataAdaptors) {
				
				FormulaProcess formulaProcess = new FormulaProcess(iDataAdaptor, formula);
				IDataAdaptor result=formulaProcess.Process();
				
				formulaAdaptors.add(result);
			}
			dataAdaptors=formulaAdaptors;
			return true;
		} catch (Exception e) {
			taskLog.addMessage("error in do dormula ");
			taskLog.addMessage(e.getMessage());
			taskLog.addMessage(Arrays.toString(e.getStackTrace()));
			taskLog.setHasError(true);
			e.printStackTrace();
		}
		
		return true;
	}
	
	
	
	private boolean generatePayload(IDataAdaptor dataAdaptor) throws DocumentException
	{
		taskLog.addMessage("begin to generate payload");
		
		payloads = new ArrayList<String>();
		
		String template = operation.getSoapTemplate();
		Map<String, String> mapping = operation.getMapping();

		List<String[]> content = dataAdaptor.getContent();

		for (String[] row : content) {
			XMLBase xmlUtil = new XMLBase();
			xmlUtil.readStringToXML(template);

			for (Map.Entry<String, String> entry : mapping.entrySet()) {
				
				String[] value = entry.getValue().split("\\|");
				if (value.length == 2 && "header".equals(value[0])) {
					int counter = -1;
					for (String header : dataAdaptor.getHeader()) {
						counter++;
						if(header.equalsIgnoreCase(value[1]))
						{
							
							break;
						}
						
					}
					if(counter==-1)
					{
						taskLog.setHasError(true);
						taskLog.addMessage("has error in mapping header");
						return false;
					}else {
						if(row.length>counter)
						{
							xmlUtil.setSingleStringValue(entry.getKey(), row[counter]);
						}else {
							taskLog.setHasError(true);
							taskLog.addMessage("got error in configure mapping or error data,Please check no empty row!");
							return false;
						}
						
						
					}
					
				}else{
				taskLog.setHasError(true);
				taskLog.addMessage("mapping header error");
				return false;
					
				}
			}
			taskLog.addMessage("generate payload success...");
			payloads.add(xmlUtil.toString());
			taskLog.addMessage(xmlUtil.toString());
		}
		
		return true;
	}
	

	
	private boolean sendPayload() throws DocumentException, UnsupportedOperationException, SOAPException, IOException
	{
		
		for (IDataAdaptor dataAdaptor : dataAdaptors) {
			
			if(!generatePayload(dataAdaptor))
			{
				taskLog.addMessage("error in generating payload for dataAdaptor");
				return false;
			}
			
			for (String payload : payloads) {
				
				if(!sendSoap(payload))
				{
					taskLog.addMessage("error in send Soap, server return error message!");
					return false;
				}
			}
			
		}
	
		return true;
	}
	

	public boolean sendSoap(String payload) throws UnsupportedOperationException, SOAPException, IOException, DocumentException {
		SoapConnection connection = operation.getSoapConnectionData();
		taskLog.addMessage("begin to send soap");
		taskLog.addMessage("soapendpoint:"+connection.getEndpoint());
		taskLog.addMessage("user name:"+connection.getUserName());
		taskLog.addMessage("password:"+connection.getPassword());
		taskLog.addMessage("password type:"+connection.getPasswordType());
		String result = soapUtil.SendSoapBase(payload, connection.getEndpoint(), connection.getUserName(), connection.getPassword(),
				connection.getPasswordType());
		taskLog.addMessage(result);
		taskLog.addMessage("sending success");
		return errorHandle(result);
	}
	
	/**
	 *  all is true
	 * @param response
	 * @return
	 * @throws DocumentException 
	 */
	private boolean errorHandle(String response) throws DocumentException
	{
		
		String path=task.getErrorPath();
		
		String value=task.getErrorValue();
		
		if(path!=null &&!path.isEmpty())
		{
			XMLBase base=new XMLBase();
			base.readStringToXML(response);
			List<String> result=new ArrayList<String>();
			
			try {
				result= base.getValuesByXpath(path);
			} catch (Exception e) {
				taskLog.addMessage("error in setting xpath or do not find node, task will continue!");
				taskLog.addMessage(e.getMessage());
			}
			
			
			if(!result.isEmpty())
			{
				
				if(value==null||value=="")
				{
					taskLog.addMessage("error handle node:"+path);
					return false;
				}
				
				for (String responseValue : result) {
					
					if(responseValue.contains(value)){
						taskLog.addMessage("error handle node:"+path+"equals "+value);
						return false;
					}
				}
			}
			
		}
		
		return true;
	}


}
